package org.ourgrid.common.executor.vbox;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import org.ourgrid.common.executor.ExecutorException;
import org.ourgrid.common.executor.ExecutorResult;
import org.ourgrid.common.executor.FolderBasedSandboxedUnixEnvironmentUtil;
import org.ourgrid.common.executor.SandBoxEnvironment;
import org.ourgrid.common.executor.config.ExecutorConfiguration;
import org.ourgrid.common.executor.config.VMWareExecutorConfiguration;
import org.ourgrid.common.executor.vserver.ProcessUtil;
import org.ourgrid.worker.WorkerConstants;

import br.edu.ufcg.lsd.commune.container.logging.CommuneLogger;

public class VBoxSandBoxEnviroment implements SandBoxEnvironment {
	
	
	private ExecutorConfiguration configuration;
	private VBoxCommandFactory commandFactory;
	
	private FolderBasedSandboxedUnixEnvironmentUtil unixFolderUtil = new FolderBasedSandboxedUnixEnvironmentUtil();
	private String hostPlaypenPath;
	
	/*
	 * These file contains the standard and error execution output. Also the
	 * execution exit value
	 */
	private File stdOutput;
	private File errorOutput;
	private File exitValueOutput;
	private File terminated;
	
	private CommuneLogger logger;
	
	public VBoxSandBoxEnviroment(CommuneLogger logger) {
		this.logger = logger;
	}
	
	
	public void setConfiguration(ExecutorConfiguration executorConfiguration) {
		this.configuration = executorConfiguration;
		this.commandFactory = new VBoxCommandFactory(configuration);
	}

	public void initSandboxEnvironment(Map<String, String> envVars)
			throws ExecutorException {
		this.hostPlaypenPath = envVars.get(WorkerConstants.ENV_PLAYPEN);
		
		this.stdOutput = new File(
				hostPlaypenPath,
				configuration.
						getProperty(VMWareExecutorConfiguration.PROPERTIES.APP_STD_OUTPUT_FILE_NAME.toString()));
		this.errorOutput = new File(
				hostPlaypenPath,
				configuration.
						getProperty(VMWareExecutorConfiguration.PROPERTIES.APP_STD_ERROR_FILE_NAME.toString()));
		this.exitValueOutput = new File(
				hostPlaypenPath,
				configuration.
						getProperty(VMWareExecutorConfiguration.PROPERTIES.APP_STD_EXITVALUE_FILE_NAME.toString()));
		
		this.terminated = new File(hostPlaypenPath + File.separator + ".terminated");
	}

	public void beginAllocation() throws ExecutorException {
		//do nothing
	}

	public void executeRemoteCommand(String dirName, String command,
			Map<String, String> envVars) throws ExecutorException {
		if(logger != null)
			logger.info( "Asked to run remote command " + command);
		
		executeRemoteCommand(command, envVars);
	}

	private void executeRemoteCommand(String command,
			Map<String, String> envVars) throws ExecutorException {
		try {
			commandFactory.createExecutionScriptCmd(command, envVars);
		} catch (IOException e) {
			throw new ExecutorException(e);
		}
		
		String errorMessage = "Could not init VBox Virtual Machine";
		ProcessUtil.buildAndRunProcessNoWait(commandFactory.createInitCommand(), errorMessage);
		
		
		while(!hasExecutionFinished()){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				throw new ExecutorException(e);
			}
		}
		
	}

	public void finishExecution() throws ExecutorException {
		//not implemented
	}

	public ExecutorResult getResult() throws ExecutorException {
		if(logger != null)
			logger.debug( "Getting result of execution..." );
		
		ExecutorResult result = new ExecutorResult();

		try {
			unixFolderUtil.catchOutputFromFile(result, stdOutput, errorOutput,
					exitValueOutput);
		} catch (Exception e) {
			throw new ExecutorException("Unable to catch output ", e);
		}

		return result;
		
	}

	public boolean hasExecutionFinished() throws ExecutorException {
		return terminated.exists();
	}

	public void kill() throws ExecutorException {
		String errorMsg = "Could not stop the Virtual Machine";
		ProcessUtil.buildAndRunProcessNoWait(commandFactory.createStopCommand(), errorMsg);
	}

	public void shutDownSandBoxEnvironment() throws ExecutorException {
		terminated.delete();  
		kill();
	}

}
